package cn.zjj.basic.service.impl;

import cn.zjj.basic.constans.VerifyCodeConstants;
import cn.zjj.basic.constans.WxConstants;
import cn.zjj.basic.dto.LoginDto;
import cn.zjj.basic.dto.LogininfoDto;
import cn.zjj.basic.exception.BusinessException;
import cn.zjj.basic.jwt.JwtUtils;
import cn.zjj.basic.jwt.RsaUtils;
import cn.zjj.basic.service.ILoginService;
import cn.zjj.basic.util.*;
import cn.zjj.org.mapper.EmployeeMapper;
import cn.zjj.system.domain.Menu;
import cn.zjj.user.domain.Logininfo;
import cn.zjj.user.domain.User;
import cn.zjj.user.domain.WeChatUser;
import cn.zjj.user.dto.WeChatBinderDto;
import cn.zjj.user.dto.WeChatLoginDto;
import cn.zjj.user.mapper.LogininfoMapper;
import cn.zjj.user.mapper.UserMapper;
import cn.zjj.user.mapper.WeChatUserMapper;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.security.PrivateKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private LogininfoMapper logininfoMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private WeChatUserMapper weChatUserMapper;

    @Autowired
    private EmployeeMapper employeeMapper;

    @Autowired
    private UserMapper userMapper;

    @Override
    public LoginDto account(LogininfoDto logininfoDto) {
       //校验空值
        String account = logininfoDto.getAccount();
        String password = logininfoDto.getPassword();
        if(StringUtils.isEmpty(account)){
            throw new BusinessException("账号不能为空");
        }else if(StringUtils.isEmpty(password)){
            throw new BusinessException("密码不能为空");
        }
        //根据account和type判断是否存在这个账户
       Logininfo logininfo= logininfoMapper.selectByAccount(logininfoDto);
        if(logininfo==null){
            throw new BusinessException("账号或密码错误,请重试!");
        }
        //校验密码是否正确
        //拿到用户传来的密码,通过查到的账户盐值合并加密,与查到的账户密码对比(注意看实现注册时给的密码是先密码还是先盐值合并之后加密的)
        String md5 = Md5Utils.encrypByMd5(password + logininfo.getSalt());
        if(!logininfo.getPassword().equals(md5)){
            throw new BusinessException("账号或密码错误,请重试!");
        }
        //账户密码都正确,通过uuid生成token传回给前端,并把用户信息和token作为key存入redis中设置登录过期时间,下次访问页面请求直接验证token过期与否
        /*UUID uuid = UUID.randomUUID();
        String token = uuid.toString();
        //redis存对象需要序列化,直接domain继承basedomain,在basedomain中序列化implements Serializable(比较落后)
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);

        //放入dto返回给前端之前,剔除其中logininfo的盐值和密码字段,
        logininfo.setPassword(null);
        logininfo.setSalt(null);

        LoginDto loginDto=new LoginDto();
        loginDto.setToken(token);
        loginDto.setLogininfo(logininfo);*/
        //弃用redis 改用jwt
        LoginDto loginDto = loginSuccessJwtHandler(logininfo);


        return loginDto;
    }

    private LoginDto loginSuccessJwtHandler(Logininfo logininfo){
        try {
            LoginData loginData = new LoginData();
            //logininfo不管是不是工作人员都需要放,所以有就先放
            loginData.setLogininfo(logininfo);

            LoginDto loginDto = new LoginDto();

            //只有工作人员登录后台才需要写具体权限,所以需要判断type
            if(logininfo.getType()==0){//工作人员
                //需要通过emp查,且只有emp需要具体权限,所以再emp写方法
                List<Menu> menus=employeeMapper.getMenusByLogininfoid(logininfo.getId());
                List<String> permissions = employeeMapper.getPermissionsBylogininfoid(logininfo.getId());
                loginData.setMenus(menus);
                loginData.setPermissions(permissions); //logdata已经放完

                loginDto.setMenus(menus);
                loginDto.setPermissions(permissions); //logindto还差jwtToken没放
            }
            //接下来是 不管是不是员工都要做的步骤
            //使用jwt代替redis传输数据
            //获取私钥,用于下面加密获取token    .获取resource下的auth_rsa.pri   即私钥
            PrivateKey privateKey = RsaUtils.getPrivateKey(RsaUtils.class.getClassLoader().getResource("auth_rsa.pri").getFile());
            //获取jwtToken
            String jwtToken = JwtUtils.generateTokenExpireInMinutes(loginData, privateKey, 300);//测试防止过期先设置三百分钟

            loginDto.setToken(jwtToken);  //logindata包含在了token中
            //放入dto返回给前端之前,剔除其中logininfo的盐值和密码字段,
            logininfo.setPassword(null);
            logininfo.setSalt(null);

            loginDto.setLogininfo(logininfo); //放了jwtToken和logininfo ,只是多了两个参数,menus和permission

            return loginDto;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }



    //微信扫码登录
    @Override
    public JsonResult wechatLogin(String code) {
     String url =  WxConstants.GET_ACK_URL
                .replace("APPID",WxConstants.APPID)
                .replace("SECRET",WxConstants.SECRET)
                .replace("CODE",code);//将链接中需要替换的东西替换掉
        //向微信发送请求,获得字符串类型的json对象
        String jsonStr = HttpUtil.httpGet(url);
        //将字符串类型的json对象转换成json对象
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        //取出其中需要的openid和token
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");

        //openid是微信用户唯一标识符,通过它去查数据库,有则是已经绑定过账户的用户,直接放行免密登录,无则绑定账户操作

        if(weChatUserMapper.selectByOpenid(openid)!=null){//数据库有信息,是绑定过的用户,直接免密登录
           /* UUID uuid = UUID.randomUUID();
            String token = uuid.toString();*/
            WeChatUser weChatUser = weChatUserMapper.selectByOpenid(openid);
            //查到相应的logininfo,放到redis中,根据userid查
            Long dbuser_id = weChatUser.getUser_id();
            Logininfo logininfo = logininfoMapper.selectByUserId(dbuser_id);

           /* //redis存对象需要序列化,直接domain继承basedomain,在basedomain中序列化implements Serializable(比较落后)
            redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);

            //放入dto返回给前端之前,剔除其中logininfo的盐值和密码字段,
            logininfo.setPassword(null);
            logininfo.setSalt(null);

            LoginDto loginDto=new LoginDto();
            loginDto.setToken(token);
            loginDto.setLogininfo(logininfo);*/
            //弃用redis,改用jwt
            LoginDto loginDto = loginSuccessJwtHandler(logininfo);

            return JsonResult.me().setResultObj(loginDto);
        }else{
            //没绑定的用户,返回wxtoken和openid回页面,让他绑定,将前面两个数据连同他绑定的数据一起再发到后端的绑定接口
            WeChatLoginDto weChatLoginDto = new WeChatLoginDto();
            weChatLoginDto.setOpenid(openid);
            weChatLoginDto.setAccess_token(access_token);
            return JsonResult.me(false,"绑定").setResultObj(weChatLoginDto);
        }

    }

    //微信扫码登录绑定
    @Override
    public LoginDto wechatBinder(WeChatBinderDto weChatBinderDto) {
            /*  phone:"13330964748",
                verifyCode:"",
                accessToken:"",
                openId:""*/
        String accessToken = weChatBinderDto.getAccessToken();
        String openId = weChatBinderDto.getOpenId();
        String phone = weChatBinderDto.getPhone();
        String verifyCode = weChatBinderDto.getVerifyCode();
        //1.控制校验
        if(     StringUtils.isEmpty(accessToken) ||
                StringUtils.isEmpty(openId) ||
                StringUtils.isEmpty(phone) ||
                StringUtils.isEmpty(verifyCode)
        ){
            throw new BusinessException("参数不能为空");
        }
        //2.校验验证码是否过期和正确
        Object o = redisTemplate.opsForValue().get(VerifyCodeConstants.BINDER_CODE_PREFIX + phone);
        if(o==null){
            throw new BusinessException("验证码过期");
        }
        //与value截取出来的验证码不区分大小写对比
        if(!verifyCode.equalsIgnoreCase(o.toString().split(":")[0])){
            throw new BusinessException("验证码错误");
        }
        //3.向微信平台发送请求获取wx个人信息
        String jsonStr = HttpUtil.httpGet(WxConstants.GET_USER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openId)
                );
        //返回的是json字符串,转为对象
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        //将信息设置到微信用户对象中
        WeChatUser weChatUser = jsonObjSetWeChatUserMsg(jsonObject);
        //再通过手机查询User表,能查到直接绑定Wx表
        User dbuser = userMapper.selectByPhone(phone);
        if(dbuser==null){
            //如果user没有信息, 将生成的信息设置到dbuser对象中去
            dbuser = phoneSetUserMsg(phone);
            //再根据dbuser生成对应的logininfo对象
          Logininfo dblogininfo = dbUserSetUserMsg(dbuser);
          //先保存logininfo,返回id
            logininfoMapper.insert(dblogininfo);
            //把id设置到dbuser
            dbuser.setLogininfo_id(dblogininfo.getId());
            //再把dbuser保存到数据库,且返回id给微信对象用
            userMapper.insert(dbuser);
        }
        //user表中有,就直接把id和wx表的userid绑定
        weChatUser.setUser_id(dbuser.getId());
        //再添加到数据库
        weChatUserMapper.insert(weChatUser);


        Logininfo logininfo = logininfoMapper.selectById(dbuser.getLogininfo_id());
        //免密登录
        /*UUID uuid = UUID.randomUUID();
        String token = uuid.toString();
        //redis存对象需要序列化,直接domain继承basedomain,在basedomain中序列化implements Serializable(比较落后)
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);

        //放入dto返回给前端之前,剔除其中logininfo的盐值和密码字段,
        logininfo.setPassword(null);
        logininfo.setSalt(null);

        LoginDto loginDto=new LoginDto();
        loginDto.setToken(token);
        loginDto.setLogininfo(logininfo);*/
        //弃用redis,改用jwt
        LoginDto loginDto = loginSuccessJwtHandler(logininfo);

        return loginDto;
    }

    //新用户user对象
    private User phoneSetUserMsg(String phone) {
        User user = new User();
        user.setUsername(phone);
        user.setPhone(phone);
        // 生成一个颜值， 密码
        String salt = StrUtils.getComplexRandomString(32);
        user.setSalt(salt);

        // 生一个密码
        String password = StrUtils.getComplexRandomString(6);
        //可以选择发短信通知给用户

        // 加密
        String dbPassword = Md5Utils.encrypByMd5(password+salt);
        user.setPassword(dbPassword);

        return user;
    }
    //新用户logininfo对象
    private Logininfo dbUserSetUserMsg(User dbuser) {
        Logininfo logininfo = new Logininfo();
        BeanUtils.copyProperties(dbuser,logininfo);
        return logininfo;
    }

    //新用户微信user对象
    private WeChatUser jsonObjSetWeChatUserMsg(JSONObject jsonObject) {
        WeChatUser weChatUser = new WeChatUser();
        weChatUser.setAddress(jsonObject.getString("country"));
        weChatUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        weChatUser.setNickname(jsonObject.getString("nickname")); // 昵称，特殊字符
        weChatUser.setOpenid(jsonObject.getString("openid"));
        weChatUser.setSex(jsonObject.getInteger("sex"));
        weChatUser.setUnionid(jsonObject.getString("unionid"));
        return weChatUser;
    }
}
